package com.efast.cafe.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.efast.cafe.framework.exception.AssistantException;




/**
 * 类辅助
 *
 * User: liyd
 * Date: 2/12/14
 * Time: 10:08 PM
 */
public class ClassUtils {

    /** 日志对象 */
    private static final Logger                  LOG        = LoggerFactory
                                                                .getLogger(ClassUtils.class);

    /**
     * Map keyed by class containing CachedIntrospectionResults.
     * Needs to be a WeakHashMap with WeakReferences as values to allow
     * for proper garbage collection in case of multiple class loaders.
     */
    private static final Map<Class<?>, BeanInfo> classCache = Collections
                                                                .synchronizedMap(new WeakHashMap<Class<?>, BeanInfo>());

    /**
     * 获取类本身的BeanInfo，不包含父类属性
     * 
     * @param clazz
     * @return
     */
    public static BeanInfo getBeanInfo(Class<?> clazz, Class<?> stopClazz) {
        try {
            BeanInfo beanInfo;
            if (classCache.get(clazz) == null) {
                beanInfo = Introspector.getBeanInfo(clazz, stopClazz);
                classCache.put(clazz, beanInfo);
                // Immediately remove class from Introspector cache, to allow for proper
                // garbage collection on class loader shutdown - we cache it here anyway,
                // in a GC-friendly manner. In contrast to CachedIntrospectionResults,
                // Introspector does not use WeakReferences as values of its WeakHashMap!
                Class<?> classToFlush = clazz;
                do {
                    Introspector.flushFromCaches(classToFlush);
                    classToFlush = classToFlush.getSuperclass();
                } while (classToFlush != null);
            } else {
                beanInfo = classCache.get(clazz);
            }
            return beanInfo;
        } catch (IntrospectionException e) {
            LOG.error("获取BeanInfo失败", e);
            throw new AssistantException(e);
        }
    }

    /**
     * 获取类的BeanInfo,包含父类属性
     * 
     * @param clazz
     * @return
     */
    public static BeanInfo getBeanInfo(Class<?> clazz) {

        return getBeanInfo(clazz, Object.class);
    }

    /**
     * 获取类本身的BeanInfo，不包含父类属性
     * 
     * @param clazz
     * @return
     */
    public static BeanInfo getSelfBeanInfo(Class<?> clazz) {

        return getBeanInfo(clazz, clazz.getSuperclass());
    }

    /**
     * 初始化实例
     * 
     * @param clazz
     * @return
     */
    public static Object newInstance(Class<?> clazz) {
        try {
            return clazz.newInstance();
        } catch (Exception e) {
            LOG.error("根据class创建实例失败", e);
            throw new AssistantException(e);
        }
    }

    /**
     * 初始化实例
     *
     * @param clazz
     * @return
     */
    public static Object newInstance(String clazz) {

        try {
            Class<?> loadClass = getDefaultClassLoader().loadClass(clazz);
            return loadClass.newInstance();
        } catch (Exception e) {
            LOG.error("根据class名称创建实例失败", e);
            throw new AssistantException(e);
        }
    }

    /**
     * 加载类
     * 
     * @param clazz
     * @return
     */
    public static Class<?> loadClass(String clazz) {
        try {
            Class<?> loadClass = getDefaultClassLoader().loadClass(clazz);
            return loadClass;
        } catch (Exception e) {
            LOG.error("根据class名称加载class失败", e);
            throw new AssistantException(e);
        }
    }

    /**
     * 当前线程的classLoader
     * 
     * @return
     */
    public static ClassLoader getDefaultClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }
    
    public static List<Class> getAllClassByParentClass(Class clazz){
		try {
			List<Class> list = new ArrayList<>();
			List<Class> allClass = getAllClass(clazz.getPackage().getName());
            /**
             * 循环判断路径下的所有类是否继承了指定类
             * 并且排除父类自己
             */
            for (int i = 0; i < allClass.size(); i++) {
                if (clazz.isAssignableFrom(allClass.get(i))) {
                    if (!clazz.equals(allClass.get(i))) {//自身并不加进去
                        list.add(allClass.get(i));
                    }else {
                    		continue;
                    }
                }
            }
            return list;
        } catch (Exception e) {
            System.out.println("出现异常");
        }
		return null;
		
	}
	public static ArrayList<Class> getAllClassByType(Class clazz){
        ArrayList<Class> list = new ArrayList<>();
        //获取指定接口的实现类
        if (clazz.isInterface()) {
            try {
                ArrayList<Class> allClass = getAllClass(clazz.getPackage().getName());
                /**
                 * 循环判断路径下的所有类是否实现了指定的接口
                 * 并且排除接口类自己
                 */
                for (int i = 0; i < allClass.size(); i++) {
                    /**
                     * 判断是不是同一个接口
                     * isAssignableFrom该方法的解析，请参考博客：
                     * http://blog.csdn.net/u010156024/article/details/44875195
                     */
                    if (clazz.isAssignableFrom(allClass.get(i))) {
                        if (!clazz.equals(allClass.get(i))) {//自身并不加进去
                            list.add(allClass.get(i));
                        }else {

                        }
                    }
                }
            } catch (Exception e) {
                System.out.println("出现异常");
            }
            //如果不是接口，则获取它的所有子类
        }else{
            
        }
        return list;
    }
	
    /**
     * 从一个指定路径下查找所有的类
     * @param name
     */
    @SuppressWarnings("rawtypes")
    private static ArrayList<Class> getAllClass(String packagename) {
        ArrayList<Class> list = new ArrayList<>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packagename.replace('.', '/');
        try {
            ArrayList<File> fileList = new ArrayList<>();
            /**
             * 这里面的路径使用的是相对路径
             * 如果大家在测试的时候获取不到，请理清目前工程所在的路径
             * 使用相对路径更加稳定！
             * 另外，路径中切不可包含空格、特殊字符等！
             * 本人在测试过程中由于空格，吃了大亏！！！
             */
            Enumeration<URL> enumeration = classLoader.getResources(path);
            while (enumeration.hasMoreElements()) {
                URL url = enumeration.nextElement();
                fileList.add(new File(url.getFile()));
            }
            for (int i = 0; i < fileList.size(); i++) {
                list.addAll(findClass(fileList.get(i),packagename));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 如果file是文件夹，则递归调用findClass方法，或者文件夹下的类
     * 如果file本身是类文件，则加入list中进行保存，并返回
     * @param file
     * @param packagename
     * @return
     */
    @SuppressWarnings("rawtypes")
    private static ArrayList<Class> findClass(File file,String packagename) {
        ArrayList<Class> list = new ArrayList<>();
        if (file == null || !file.exists()) {
            return list;
        }
        File[] files = file.listFiles();
        if (files != null) {
        	for (File file2 : files) {
                if (file2.isDirectory()) {
                    assert !file2.getName().contains(".");//添加断言用于判断
                    ArrayList<Class> arrayList = findClass(file2, packagename+"."+file2.getName());
                    list.addAll(arrayList);
                }else if(file2.getName().endsWith(".class")){
                    try {
                        //保存的类文件不需要后缀.class
                        list.add(Class.forName(packagename + '.' + file2.getName().substring(0,
                                file2.getName().length()-6)));
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return list;
    }
}
